package com.coalmine.activiti.flow.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.coalmine.activiti.api.dto.FlowEntryDTO;
import com.coalmine.activiti.api.dto.TaskCommentDTO;
import com.coalmine.activiti.api.vo.FlowCategoryVo;
import com.coalmine.activiti.api.vo.FlowEntryVo;
import com.coalmine.activiti.api.vo.FlowTaskCommentVo;
import com.coalmine.activiti.api.vo.FlowWorkOrderVo;
import com.coalmine.activiti.common.core.annotation.MyRequestBody;
import com.coalmine.activiti.common.core.object.MyPageData;
import com.coalmine.activiti.common.core.object.MyPageParam;
import com.coalmine.activiti.common.core.util.MyModelUtil;
import com.coalmine.activiti.common.core.util.MyPageUtil;
import com.coalmine.activiti.common.flow.constant.FlowConstant;
import com.coalmine.activiti.common.flow.model.FlowCategory;
import com.coalmine.activiti.common.flow.model.FlowEntry;
import com.coalmine.activiti.common.flow.model.FlowTaskComment;
import com.coalmine.activiti.common.flow.model.FlowWorkOrder;
import com.coalmine.activiti.common.flow.service.*;
import com.coalmine.activiti.common.flow.vo.FlowTaskVo;
import com.coalmine.activiti.flow.util.FileUtil;
import com.coalmine.activiti.flow.vo.NodeInfoVo;
import com.coalmine.activiti.webadmin.upms.service.IActDeBusinessService;
import com.coalmine.common.core.domain.R;
import com.coalmine.common.core.utils.SecurityUtils;
import com.coalmine.common.core.utils.StringUtils;
import com.coalmine.common.core.web.domain.AjaxResult;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
import org.activiti.engine.task.Task;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@RestController
@RequestMapping("/flowInfo")
public class FlowInfoController {

    @Autowired
    private FlowApiService flowApiService;

    @Autowired
    private IActDeBusinessService actDeBusinessService;

    @Autowired
    private FlowCategoryService flowCategoryService;

    @Autowired
    private FlowEntryService flowEntryService;

    @Autowired
    private FlowTaskCommentService flowTaskCommentService;

    @Autowired
    private FlowWorkOrderService flowWorkOrderService;

    /**
     * 流程节点信息
     *
     * @param tableName
     * @return
     */
    @GetMapping("/getProcessAllElements")
    public AjaxResult getProcessAllElements(@RequestParam String tableName, @RequestParam String type) {
        // 获取流程标识Key
        String processDefinitionKey = actDeBusinessService.findDefinitionKeyByTableName(tableName, type);
        if (StringUtils.isEmpty(processDefinitionKey)) {
            String errorMessage = "processDefinitionKey为空，不能启动新流程！";
            log.info(errorMessage);
            return AjaxResult.success();
            //return AjaxResult.error(errorMessage);
        }

        String definitionId = actDeBusinessService.findDefinitionId(processDefinitionKey);

        Collection<FlowElement> flowElements = flowApiService.getProcessAllElements(definitionId);

        List<NodeInfoVo> list = new ArrayList<NodeInfoVo>();
        for (FlowElement flowElement : flowElements) {
            if (flowElement instanceof UserTask) {
            	UserTask userTask=(UserTask)flowElement;
            	if (FlowConstant.START_USER_NAME_VAR.equals(userTask.getAssignee())) {
					continue;
				}
                NodeInfoVo vo = new NodeInfoVo();
                vo.setNodeId(userTask.getId());
                vo.setNodeName(userTask.getName());
                vo.setType("0");
                if (((UserTask) flowElement).getBehavior() instanceof ParallelMultiInstanceBehavior) {
                    vo.setType("1");
                }
                list.add(vo);
            }
        }
        return AjaxResult.success(list);
    }

    /**
     * 流程节点信息
     *
     * @param processDefinitionKey 流程标识
     * @return
     */
    @GetMapping("v2/getProcessAllElements")
    public AjaxResult processAllElements(@RequestParam String processDefinitionKey) {
        // 获取流程标识Key
        if (StringUtils.isEmpty(processDefinitionKey)) {
            String errorMessage = "processDefinitionKey为空，不能启动新流程！";
            log.info(errorMessage);
            return AjaxResult.success();
        }

        String definitionId = actDeBusinessService.findDefinitionId(processDefinitionKey);
        if (StringUtils.isEmpty(definitionId)) {
            String errorMessage = "流程key错误，没有对应的流程！";
            log.info(errorMessage);
            return AjaxResult.success();
        }

        Collection<FlowElement> flowElements = flowApiService.getProcessAllElements(definitionId);

        List<NodeInfoVo> list = new ArrayList<NodeInfoVo>();
        for (FlowElement flowElement : flowElements) {
            if (flowElement instanceof UserTask) {
                UserTask userTask=(UserTask)flowElement;
                if (FlowConstant.START_USER_NAME_VAR.equals(userTask.getAssignee())) {
                    continue;
                }
                NodeInfoVo vo = new NodeInfoVo();
                vo.setNodeId(userTask.getId());
                vo.setNodeName(userTask.getName());
                vo.setType("0");
                if (((UserTask) flowElement).getBehavior() instanceof ParallelMultiInstanceBehavior) {
                    vo.setType("1");
                }
                list.add(vo);
            }
        }
        return AjaxResult.success(list);
    }


    /**
     * 获取指定流程定义的流程图。
     *
     * @param tableName 表名
     * @return 流程图。
     */
    @GetMapping("/viewProcessBpmn")
    public void viewProcessBpmn(@RequestParam String tableName, @RequestParam String type, HttpServletResponse resp) throws IOException {
        // 获取流程标识Key
        String processDefinitionKey = actDeBusinessService.findDefinitionKeyByTableName(tableName, type);
        if (StringUtils.isEmpty(processDefinitionKey)) {
            String errorMessage = "流程业务配置错误，没有对应的流程！";
            log.info(errorMessage);
            return;
        }

        String processDefinitionId = actDeBusinessService.findDefinitionId(processDefinitionKey);

        BpmnModel bpmnModel = flowApiService.getBpmnModelByDefinitionId(processDefinitionId);

        DefaultProcessDiagramGenerator ge = new DefaultProcessDiagramGenerator();
        InputStream inputStream = ge.generateDiagram(bpmnModel, new ArrayList<>(), new ArrayList<>(), "宋体", "宋体", null, false);
        FileUtil.writeFile(resp, inputStream);
    }

    /**
     * 获取指定流程定义的流程图。
     *
     * @param processDefinitionKey 表名
     * @return 流程图。
     */
    @GetMapping("/v2/viewProcessBpmn")
    public void processBpmn(@RequestParam String processDefinitionKey, HttpServletResponse resp) throws IOException {
        // 获取流程标识Key
        String processDefinitionId = actDeBusinessService.findDefinitionId(processDefinitionKey);
        if (StringUtils.isEmpty(processDefinitionId)) {
            String errorMessage = "流程业务配置错误，没有对应的流程！";
            log.info(errorMessage);
            return;
        }

        BpmnModel bpmnModel = flowApiService.getBpmnModelByDefinitionId(processDefinitionId);

        DefaultProcessDiagramGenerator ge = new DefaultProcessDiagramGenerator();
        InputStream inputStream = ge.generateDiagram(bpmnModel, new ArrayList<>(), new ArrayList<>(), "宋体", "宋体", null, false);
        FileUtil.writeFile(resp, inputStream);
    }


    /**
     * 获取当前用户是否可审批。
     */
    @GetMapping("/checkApproveByBizId")
    public R<Boolean> checkApproveByBizId(@RequestParam(value = "bizId") String bizId) {
        Boolean result = actDeBusinessService.checkApproveByBizId(bizId);
        return R.ok(result);
    }

    /**
     * 获取下一个审批人。
     */
    @GetMapping("/getNextApproveByBizId")
    public R<List<String>> getNextApproveByBizId(@RequestParam(value = "bizId") String bizId) {
    	List<String> result = actDeBusinessService.getNextApproveByBizId(bizId);
        return R.ok(result);
    }

    /**
     * 获取工作流类型。
     * @param name 类型名称。
     * @param code 类型码值
     * @return 工作类型集合
     */
    @GetMapping("/categoryList")
    public R<List<FlowCategoryVo>> categoryList(String name, String code) {
        FlowCategory flowCategoryFilter = new FlowCategory();
        flowCategoryFilter.setName(name);
        flowCategoryFilter.setCode(code);
        List<FlowCategory> result = flowCategoryService.getFlowCategoryList(flowCategoryFilter,"show_order");
        return R.ok(MyModelUtil.copyCollectionTo(result,FlowCategoryVo.class));
    }


    /**
     * 获取工作流类型
     * @param flowEntryDTO 流对象DTO
     * @return 工作流配置信息集合
     */
    @PostMapping("/flowEntryList")
    public R<List<FlowEntryVo>> flowEntryList(@RequestBody FlowEntryDTO flowEntryDTO) {
        FlowEntry flowEntryFilter = MyModelUtil.copyTo(flowEntryDTO, FlowEntry.class);
        List<FlowEntry> result = flowEntryService.getFlowEntryList(flowEntryFilter,"entry_id");
        return R.ok(MyModelUtil.copyCollectionTo(result, FlowEntryVo.class));
    }

    /**
     * 流程审批意见
     * @param taskCommentDTO 流对象DTO
     * @return 工作流配置信息集合
     */
    @PostMapping("/approvalComments")
    public R<List<FlowTaskCommentVo>> getApprovalComments(@RequestBody TaskCommentDTO taskCommentDTO) {
        //工作流工单实体对象
        String errorMessage;
        if(StrUtil.isBlank(taskCommentDTO.getBusinessKey())){
            errorMessage = "必传参数业务ID[businessKey]为空！";
            log.info(errorMessage);
            return R.fail(null, errorMessage);
        }
        FlowWorkOrder flowWorkOrderFilter = MyModelUtil.copyTo(taskCommentDTO, FlowWorkOrder.class);
        List<FlowWorkOrder> flowWorkOrderList = flowWorkOrderService.getFlowWorkOrderList(flowWorkOrderFilter,"create_time");
        if(CollectionUtils.isEmpty(flowWorkOrderList)){
            errorMessage = "工作流工单实体对象为空，不能获取审批意见！";
            log.info(errorMessage);
            return R.fail(null, errorMessage);
        }
        log.info("实例Id:{}",flowWorkOrderList.get(0).getProcessInstanceId());
        FlowTaskComment filter = MyModelUtil.copyTo(taskCommentDTO, FlowTaskComment.class);
        filter.setProcessInstanceId(flowWorkOrderList.get(0).getProcessInstanceId());
        List<FlowTaskComment> result = flowTaskCommentService.getListByFilter(filter);
        List<FlowTaskCommentVo> resp = MyModelUtil.copyCollectionTo(result, FlowTaskCommentVo.class);
        if(flowWorkOrderList.size()==0){
            errorMessage = "未查找到该流程实体对象的审批意见！";
            log.info(errorMessage);
            return R.fail(null, errorMessage);
        }
        return R.ok(resp);
    }


    /**
     * 获取工作流实例状态
     * @param bizId 类型名称。
     * @return 工作类型集合
     */
    @GetMapping("/workStatus")
    public R<FlowWorkOrderVo> workStatus(@RequestParam(value = "bizId")String bizId) {
        FlowWorkOrder filter = new FlowWorkOrder();
        filter.setBusinessKey(bizId);
        List<FlowWorkOrder> list = flowWorkOrderService.getFlowWorkOrderList(filter,"create_time");
        if(list.size()==0){
            String errorMessage = "流程实体对象的不存在！";
            log.info(errorMessage);
            return R.fail(null, errorMessage);
        }
        return R.ok(MyModelUtil.copyTo(list.get(list.size()-1), FlowWorkOrderVo.class));
    }


    /**
     * 获取当前用户的已办理的审批任务列表。
     *
     * @param processDefinitionName 流程名。
     * @param bizId                 业务Id。
     * @param beginDate             流程发起开始时间。
     * @param endDate               流程发起结束时间。
     * @param pageParam             分页对象。
     * @return 查询结果应答。
     */
    @PostMapping("/listHistoricTask")
    public R<MyPageData<Map<String, Object>>> listHistoricTask(
            @MyRequestBody String processDefinitionName,
            @MyRequestBody String bizId,
            @MyRequestBody String beginDate,
            @MyRequestBody String endDate,
            @MyRequestBody(required = true) MyPageParam pageParam){

        try {
            MyPageData<HistoricTaskInstance> pageData = flowApiService.getHistoricTaskInstanceFinishedList(processDefinitionName, bizId, beginDate, endDate, pageParam);
            List<Map<String, Object>> resultList = new LinkedList<>();
            pageData.getDataList().forEach(instance -> resultList.add(BeanUtil.beanToMap(instance)));
            List<HistoricTaskInstance> taskInstanceList = pageData.getDataList();
            if (CollUtil.isNotEmpty(taskInstanceList)) {
                Set<String> instanceIdSet = taskInstanceList.stream()
                        .map(HistoricTaskInstance::getProcessInstanceId).collect(Collectors.toSet());
                List<HistoricProcessInstance> instanceList = flowApiService.getHistoricProcessInstanceList(instanceIdSet);
                Map<String, HistoricProcessInstance> instanceMap =
                        instanceList.stream().collect(Collectors.toMap(HistoricProcessInstance::getId, c -> c));
                resultList.forEach(result -> {
                    HistoricProcessInstance instance = instanceMap.get(result.get("processInstanceId").toString());
                    result.put("processDefinitionKey", instance.getProcessDefinitionKey());
                    result.put("processDefinitionName", instance.getProcessDefinitionName());
                    result.put("startUser", instance.getStartUserId());
                    result.put("businessKey", instance.getBusinessKey());
                });
                Set<String> taskIdSet =
                        taskInstanceList.stream().map(HistoricTaskInstance::getId).collect(Collectors.toSet());
                List<FlowTaskComment> commentList = flowTaskCommentService.getFlowTaskCommentListByTaskIds(taskIdSet);
                Map<String, List<FlowTaskComment>> commentMap =
                        commentList.stream().collect(Collectors.groupingBy(FlowTaskComment::getTaskId));
                resultList.forEach(result -> {
                    List<FlowTaskComment> comments = commentMap.get(result.get("id").toString());
                    if (CollUtil.isNotEmpty(comments)) {
                        result.put("approvalType", comments.get(0).getApprovalType());
                        comments.remove(0);
                    }
                });
            }
            return R.ok(MyPageUtil.makeResponseData(resultList, pageData.getTotalCount()));
        } catch (ParseException e) {
            String errorMessage = errorMessage="已办流程请求,内部数据转换报错";
            log.error(errorMessage,e);
            return R.fail(errorMessage);
        }
    }


    /**
     * 返回当前用户待办的任务列表。
     *
     * @param processDefinitionKey  流程标识。
     * @param processDefinitionName 流程定义名 (模糊查询)。
     * @param taskName              任务名称 (魔术查询。
     * @param bizId                 业务Id。
     * @param pageParam             分页对象。
     * @return 返回当前用户待办的任务列表。如果指定流程标识，则仅返回该流程的待办任务列表。
     */
    @PostMapping("/listRuntimeTask")
    public R<MyPageData<FlowTaskVo>> listRuntimeTask(
            @MyRequestBody String processDefinitionKey,
            @MyRequestBody String processDefinitionName,
            @MyRequestBody String taskName,
            @MyRequestBody String bizId,
            @MyRequestBody(required = true) MyPageParam pageParam) {
        String username = SecurityUtils.getUsername();
        MyPageData<Task> pageData = flowApiService.getTaskListByUserName(
                username, processDefinitionKey, processDefinitionName, taskName, bizId, pageParam);
        List<FlowTaskVo> flowTaskVoList = flowApiService.convertToFlowTaskList(pageData.getDataList());
        return R.ok(MyPageUtil.makeResponseData(flowTaskVoList, pageData.getTotalCount()));
    }
	
	@GetMapping("/getMoreApproveByBizIds")
    public R<Map<String,List<String>>> getMoreApproveByBizId(@RequestParam(value = "bizIds") List<String> bizIds) {
    	Map<String,List<String>> result = actDeBusinessService.getNextApproveByBizId(bizIds);
        return R.ok(result);
    }
}
